feat(user-journey): capture page-view referrer for organic/referral attribution#69
Conversation
Captures document.referrer at page-load time and sends it as data.payload.referrer in every page_view event — both initial load and SPA route changes — so organic/referral attribution is recorded even when a visitor never interacts with a form. Centralises page-view creation in createPageViewEvent(), adds SSR guards for document/window/navigator/localStorage, and regenerates the bundle. Adds focused unit tests (test/unit/) and a test:unit npm script. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
| let userJourney; | ||
|
|
||
| async function loadUserJourney() { | ||
| if (userJourney) return userJourney; | ||
|
|
||
| const result = await esbuild.build({ | ||
| entryPoints: ["src/store/user-journey.ts"], | ||
| bundle: true, | ||
| format: "cjs", | ||
| platform: "browser", | ||
| target: "es2020", | ||
| write: false, | ||
| }); | ||
| const module = { exports: {} }; | ||
| new Function("module", "exports", result.outputFiles[0].text)(module, module.exports); | ||
| userJourney = module.exports; | ||
| return userJourney; |
There was a problem hiding this comment.
Shared module cache hides inter-test state leakage
loadUserJourney() evaluates the esbuild bundle exactly once and caches the result in the module-level userJourney variable. The bundled output includes identify.ts, which holds module-level mutable variables environmentId and identifyInProgress. If a future test calls identifyLead and leaves identifyInProgress = true (e.g., due to an error mid-flight), every subsequent test that calls getLeadDataWithTTL via getSurfaceLeadDataSafely will hit the waitForCachedData path and time out. Clearing shared mutable state between tests (or rebuilding the module per test) would prevent this latent coupling.
There was a problem hiding this comment.
No change here: the current page-view tests never call identifyLead, so they do not mutate identifyInProgress or environmentId. identifyLead also resets identifyInProgress in a finally block on both success and failure. The cached exports avoid rebundling for every test without introducing state leakage in this suite; future identifyLead-specific tests should own any additional isolation their lifecycle requires.
Summary
document.referrerto everypage_viewevent emitted by Surface Tag (both initial page load and SPA route changes), surfaced asdata.payload.referrer.createPageViewEvent()helper so initial-load and SPA events use the same schema consistently.document,window,navigator, andlocalStorageinuser-journey.tsandcookies.ts.test/unit/user-journey.test.jscovering: Google referrer, direct/empty referrer, no-document safety,sendBeacontransport, and SPA navigation.Backend follow-up needed: confirm the Forms lead/session ingestion pipeline persists and reads
data.payload.referrerforpage_viewevents.🤖 Generated with Claude Code